home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 4: GNU Archives / Linux Cubed Series 4 - GNU Archives.iso / gnu / fontutil.6 / fontutil / fontutils-0.6 / limn / display.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-09-06  |  21.9 KB  |  705 lines

  1. /* display.c: show output online using the X window system.
  2.  
  3. Copyright (C) 1992 Free Software Foundation, Inc.
  4.  
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2, or (at your option)
  8. any later version.
  9.  
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  18.  
  19. #include "config.h"
  20.  
  21. #include <X11/Intrinsic.h>
  22. #include <X11/StringDefs.h>
  23. #include <X11/Xaw/Label.h>
  24. #include <X11/Xaw/Viewport.h>
  25. #include <X11/Xmu/SysUtil.h>
  26.  
  27. #include "report.h"
  28. #include "spline.h"
  29. #include "vector.h"
  30.  
  31. #include "display.h"
  32. #include "main.h"
  33. #include "xserver.h"
  34.  
  35.  
  36. /* Says whether to show stuff online or not.  (-do-display)  */
  37. boolean wants_display = false;
  38.  
  39. /* How many expanded pixels we put between lines on the grid.  Even
  40.    though all this size and the others must be nonnegative, we must
  41.    declare them as integers, since we do subtractions on them that might
  42.    result in values less then zero.  (-display-grid-size) */
  43. int display_grid_size = 10;
  44.  
  45. /* Size of square we print each pixel in.  (-display-pixel-size)  */ 
  46. int display_pixel_size = 9;
  47.  
  48. /* Size of what we display inside the square for each pixel (must be
  49.    less than `display_pixel_size').  (-display-rectangle-size)  */
  50. int display_rectangle_size = 6;
  51.  
  52. /* Says whether to stop and wait for a <return> after each character. 
  53.    (-display-continue)  */
  54. boolean display_continue = false;
  55.  
  56. /* These are used for identifying the server. */
  57. Atom foserver_identity_atom;
  58.  
  59. /* The messages our server recognizes.  */
  60. Atom foserver_exit_atom, foserver_update_pixmap_atom;
  61.  
  62. /* The X state.  */
  63.  
  64. /* Where we should send our requests.  (This is information about
  65.    our own ``limn server'', not the X server.)   We need the
  66.    window only for when we send messages to our server.  */
  67. static Display *display;
  68. static Window server_window;
  69.  
  70. /* What we actually draw into.  */
  71. static Pixmap pixmap = None;
  72.  
  73. /* The GC we use for drawing.  */
  74. static GC gc;
  75.  
  76. /* Expands into the four arguments that define a rectangle for Xlib.  */
  77. #define X_RECTANGLE(c) \
  78.   (c).x, (c).y, display_rectangle_size, display_rectangle_size
  79.  
  80. /* Ditto, for lines.  */
  81. #define X_LINE(c1, c2) (c1).x, (c1).y, (c2).x, (c2).y
  82.  
  83. /* Offsets for converting Cartesian coordinates to X ones.  */
  84. static bounding_box_type offset_bb;
  85.  
  86. static coordinate_type cartesian_to_x (coordinate_type);
  87. static void digitize_spline (spline_type);
  88. static string get_identity (void);
  89. static void get_server_info (Window *, Display **, string);
  90. static coordinate_type real_cartesian_to_x (real_coordinate_type);
  91. static coordinate_type real_cartesian_to_offset_x (real_coordinate_type);
  92. static XSegment segment_to_x (XSegment);
  93. static void send_event (Window, Atom, long);
  94. static Bool window_change_p (Display *, XEvent *, char *);
  95.  
  96. /* Start things off.  */
  97.  
  98. void
  99. init_display (bitmap_font_type f)
  100. {
  101.   string server_identity;
  102.   
  103.   /* Do nothing unless the user wants us to.  */
  104.   if (!wants_display) return;
  105.   
  106.   server_identity = get_identity ();
  107.  
  108. #ifndef STANDALONE_SERVER
  109.   { /* Fork our server.  This is the production case.  */
  110.     unsigned design_size;
  111.  
  112.     int pid = fork ();
  113.     switch (pid)
  114.       {
  115.       case -1:
  116.         FATAL_PERROR ("fork");
  117.  
  118.       case 0:
  119.         { /* We are the child, i.e., the server.  Convert the design size,
  120.              which we have in points, to pixels.  `start_server' never
  121.              returns, because it waits forever for events.  */
  122.           design_size = (BITMAP_FONT_DESIGN_SIZE (f) * atof (dpi)
  123.                          / POINTS_PER_INCH);
  124.  
  125.           start_server (design_size, server_identity);
  126.           FATAL ("init_display: start_server returned");
  127.         }
  128.  
  129.       default:
  130.         /* We are the parent, i.e., the main process.
  131.            Continue outside the switch.  */
  132.         ;
  133.       }
  134.   }
  135. #endif /* not STANDALONE_SERVER */
  136.  
  137.   /* Assign to the globals so that our other routines can send messages.  */
  138.   get_server_info (&server_window, &display, server_identity);
  139.  
  140.   /* Register our message atoms with the X server.  */
  141.   foserver_exit_atom = XInternAtom (display, FOSERVER_EXIT_ATOM, False);
  142.   foserver_update_pixmap_atom
  143.     = XInternAtom (display, FOSERVER_UPDATE_PIXMAP_ATOM, False);
  144.  
  145. }
  146.  
  147.  
  148. /* Return the first child of W that has the property
  149.    FOSERVER_IDENTITY_ATOM with the attribute string S.  This is not
  150.    very efficient, but it is the only sure way.  We could have a window
  151.    manager that put a lot of windows around the display server.
  152.   
  153.    If the search does not succed we return None.  */
  154.  
  155. static Window
  156. search_children (Display *display, Window w, string s)
  157. {
  158.   Window root, parent, win;
  159.   Window *children;
  160.   unsigned nchildren;
  161.   XTextProperty textP;
  162.  
  163.   /* Test this window. */
  164.   if (XGetTextProperty (display, w, &textP, foserver_identity_atom)
  165.       && textP.encoding == XA_STRING && textP.encoding == XA_STRING
  166.       && textP.format == 8 && textP.nitems != 0
  167.       && STREQ ((string) textP.value, s))
  168.     {
  169.       XFree ((char *) textP.value);
  170.       return w; /* This is the correct window.  */
  171.     }
  172.  
  173.   /* OK, we found nothing. Recursively try the children. */
  174.   if (XQueryTree (display, w, &root, &parent, &children, &nchildren) == 0)
  175.     FATAL ("limn: XQueryTree failed on parent");
  176.  
  177.   for (; nchildren > 0; nchildren--)
  178.     if (win = search_children (display, children[nchildren-1], s))
  179.       {
  180.         XFree ((char *) children);
  181.         return win;
  182.       }
  183.   XFree ((char *) children);
  184.  
  185.   return None; /* We didn't find anything. */
  186. }
  187.  
  188.  
  189. /* Return `hostname:pid' as a string.  */
  190.  
  191. static string
  192. get_identity ()
  193. {
  194.   char s[1024];
  195.   int i;
  196.   
  197.   i = XmuGetHostname (s, sizeof (s));
  198.   if (i == 0)
  199.     FATAL ("limn: Could not get hostname");
  200.  
  201.   sprintf (&s[i], ":%d", getpid ());
  202.   return xstrdup (s);
  203. }
  204.  
  205.  
  206. /* Return the X window id for the window that the forked process should
  207.    have created.  */
  208.  
  209. static void
  210. get_server_info (Window *server_window, Display **display, string identity)
  211. {
  212.   int scr;
  213.   
  214.   /* Open our own connection to the X server, since sending messages
  215.      through the server's connection hangs.  */
  216.   *display = XOpenDisplay (NULL);
  217.   if (*display == NULL)
  218.     {
  219.       string d = getenv ("DISPLAY");
  220.       if (d == NULL)
  221.         FATAL ("limn: Could not open display, since your DISPLAY \
  222. environment variable is not set");
  223.       else
  224.         FATAL1 ("limn: Could not open display `%s'", getenv ("DISPLAY"));
  225.     }
  226.  
  227.   /* Get the identity atom. Create it if it doesn't exist.  */
  228.   foserver_identity_atom
  229.     = XInternAtom (*display, FOSERVER_IDENTITY_ATOM, False);
  230.  
  231.   /* Ask to be told about most window events.  */
  232.   XSelectInput (*display, DefaultRootWindow (*display),
  233.                 SubstructureNotifyMask);
  234.  
  235.   /* Remove potential race condition below -- if our server has already
  236.      had its window realized, we wouldn't find it otherwise. Search all
  237.      windows first.  We might just as well search all screens too. */
  238.   *server_window = None;
  239.   for (scr = ScreenCount (*display); scr > 0 && *server_window == None; scr--)
  240.     {
  241.       Window root = RootWindow (*display, scr - 1);
  242.       *server_window = search_children (*display, root, identity);
  243.     }
  244.  
  245.   while (*server_window == None)
  246.     {
  247.       XEvent event;
  248.       Window w;
  249.       
  250.       /* Wait for an event that might mean our server is ready.  */
  251.       XIfEvent (*display, &event, window_change_p, NULL);
  252.  
  253.       /* We look for some property on the window, and rely on our server
  254.          setting that property.  */
  255.       switch (event.type)
  256.         {
  257.         case MapNotify:
  258.           w = event.xmap.window;
  259.       *server_window = search_children (*display, w, identity);
  260.           break;
  261.           
  262.         default:
  263.           FATAL1 ("limn: Unexpected event (type %d)", event.type);
  264.         }
  265.     }
  266.  
  267.   /* We don't want to see any events any more.  */
  268.   XSelectInput (*display, DefaultRootWindow (*display),
  269.                 NoEventMask);
  270. }
  271.  
  272.  
  273. /* The only event we're interested in is when a window gets mapped,
  274.    because that's the last thing our server does before waiting for
  275.    events.  */
  276.  
  277. static Bool
  278. window_change_p (Display *d, XEvent *e, char *client_data)
  279. {
  280.   return e->type == MapNotify;
  281. }
  282.  
  283.  
  284. /* Tell our server to shut down, and close our own connection.  */
  285.  
  286. void
  287. close_display ()
  288. {
  289.   if (!wants_display) return;
  290.  
  291.   send_event (server_window, foserver_exit_atom, 0);
  292.  
  293.   XCloseDisplay (display);
  294. }
  295.  
  296. /* To start off a new character, we print a grid so we can locate points.  */
  297.  
  298. void 
  299. x_start_char (char_info_type c)
  300. {
  301.   XGCValues gc_values;
  302.   int x, y;
  303.   unsigned n_vlines, n_hlines;
  304.   unsigned char_width = (CHAR_BITMAP_WIDTH (c) + 2) * display_pixel_size;
  305.   unsigned max_vlines = 1 + char_width / display_grid_size;
  306.   unsigned char_height = (CHAR_BITMAP_HEIGHT (c) + 2) * display_pixel_size;
  307.   unsigned max_hlines = 1 + char_height / display_grid_size;
  308.   XSegment lines[max_vlines + max_hlines];
  309.   
  310.   if (!wants_display) return;
  311.  
  312.   /* Remember this bounding box, since we have to change coordinates
  313.      from its coordinates into X coordinates.  */
  314.   offset_bb = CHAR_BB (c);
  315.   
  316.   /* Allow for rounding errors.  */
  317.   MAX_ROW (offset_bb) += 1;
  318.   MIN_ROW (offset_bb) -= 1;
  319.   MAX_COL (offset_bb) += 1;
  320.   MIN_COL (offset_bb) -= 1;
  321.  
  322.   /* If this isn't the very first time we are called, we must free the
  323.      storage for the previous character.  */
  324.   if (pixmap != None)
  325.     {
  326.       XFreePixmap (display, pixmap);
  327.       /* The GC and the pixmap are created together, so we can free them
  328.          together.  */ 
  329.       XFreeGC (display, gc);
  330.     }
  331.  
  332.   /* We always want a Pixmap of depth one.  */
  333.   pixmap = XCreatePixmap (display, DefaultRootWindow (display),
  334.                           char_width, char_height, 1); 
  335.   
  336.   /* When drawing onto `pixmap', we want to draw in 1's, and have the
  337.      background be 0's.  The Label widget does an XCopyPlane on
  338.      monochrome Pixmaps, so this will result in the right colors on the
  339.      screen.  But we want to start with the foreground 0 (which is the
  340.      default), since the first thing we want to do is initialize it to
  341.      all white.  */
  342.   gc_values.background = 0;
  343.   gc = XCreateGC (display, pixmap, GCBackground, &gc_values);
  344.  
  345.   /* Initialize `pixmap' to white.  */
  346.   XFillRectangle (display, pixmap, gc, 0, 0, char_width, char_height);
  347.   XSetForeground (display, gc, 1);
  348.  
  349.   /* Draw the grid.  */
  350.   for (x = (MIN_COL (offset_bb) / display_grid_size) * display_grid_size,
  351.          n_vlines = 0;
  352.        x <= MAX_COL (offset_bb) && n_vlines < max_vlines;
  353.        x += display_grid_size, n_vlines++)
  354.     {
  355.       lines[n_vlines].x1 = lines[n_vlines].x2 = x;
  356.       lines[n_vlines].y1 = MAX_ROW (offset_bb);
  357.       lines[n_vlines].y2 = MIN_ROW (offset_bb);
  358.       lines[n_vlines] = segment_to_x (lines[n_vlines]);
  359.     }
  360.  
  361.   /* X considers the origin to be at the upper left.  But it is easier
  362.      to understand the display if the pixels in the row y=0 are directly 
  363.      above a grid line, rather than hanging below it.  So we offset the
  364.      y value by one.  */
  365.   for (y = ((MIN_ROW (offset_bb)) / display_grid_size) * display_grid_size,
  366.          n_hlines = 0;
  367.        y <= MAX_ROW (offset_bb) && n_hlines < max_hlines;
  368.        y += display_grid_size, n_hlines++)
  369.     {
  370.       lines[n_vlines + n_hlines].x1 = MIN_COL (offset_bb);
  371.       lines[n_vlines + n_hlines].x2 = MAX_COL (offset_bb);
  372.       lines[n_vlines + n_hlines].y1 = lines[n_vlines + n_hlines].y2 = y - 1;
  373.       lines[n_vlines + n_hlines] = segment_to_x (lines[n_vlines + n_hlines]);
  374.     }
  375.  
  376.   /* The grid is less obtrusive if we use dashed lines.  */
  377.   XSetLineAttributes (display, gc, 0, LineOnOffDash, CapButt, JoinMiter);
  378.   XDrawSegments (display, pixmap, gc, lines, n_vlines + n_hlines);
  379.   XSetLineAttributes (display, gc, 0, LineSolid, CapButt, JoinMiter);
  380. }
  381.  
  382.  
  383. /* This routine is called for every pixel in the outline of the filtered
  384.    bitmap.  */ 
  385.  
  386. void
  387. display_pixel (real_coordinate_type real_c)
  388. {
  389.   coordinate_type c;
  390.  
  391.   if (!wants_display) return;
  392.   
  393.   c = real_cartesian_to_x (real_c);
  394.   XDrawRectangle (display, pixmap, gc, X_RECTANGLE (c));
  395. }
  396.  
  397.  
  398. /* This marks the corners we find before we start the fitting.  Since
  399.    these corners are on the original pixel outline, they are integers.  */
  400.  
  401. void
  402. display_corner (coordinate_type c)
  403. {
  404.   if (!wants_display) return;
  405.  
  406.   c = cartesian_to_x (c);
  407.   XFillRectangle (display, pixmap, gc, X_RECTANGLE (c));
  408. }
  409.  
  410.  
  411. /* This marks the points at which we subdivide during the fitting. 
  412.    Since the filtering has happened now, the coordinates are not
  413.    necessarily integers.  */
  414.  
  415. void
  416. display_subdivision (real_coordinate_type real_c)
  417. {
  418.   coordinate_type c;
  419.   if (!wants_display) return;
  420.   
  421.   c = real_cartesian_to_x (real_c);
  422.   
  423.   /* So the pixel we already drew at this location doesn't still appear
  424.      behind the circle we are about to draw, thus turning the circle
  425.      into a square.  */
  426.   XSetForeground (display, gc, 0);
  427.   XFillRectangle (display, pixmap, gc, X_RECTANGLE (c));
  428.   XSetForeground (display, gc, 1);
  429.   
  430.   /* Oddly enough, the ``circle'' that's drawn is a rectangle, for small
  431.      enough sizes.  */
  432.   XFillArc (display, pixmap, gc, c.x, c.y,
  433.             display_pixel_size, display_pixel_size, 0, 360 * 64);
  434. }
  435.  
  436.  
  437. /* Here we display the character C after fitting.  The list SPLINE_LIST
  438.    is the fitted segments.  */
  439.  
  440. void
  441. x_output_char (char_info_type c, spline_list_array_type splines)
  442. {
  443.   unsigned this_list;
  444.   
  445.   if (!wants_display) return;
  446.   
  447.   /* Draw the fitted lines a little thicker, so they'll be easier to see.  */
  448.   XSetLineAttributes (display, gc, 1, LineSolid, CapButt, JoinMiter);
  449.  
  450.   for (this_list = 0; this_list < SPLINE_LIST_ARRAY_LENGTH (splines);
  451.        this_list++) 
  452.     {
  453.       unsigned this_spline;
  454.       spline_list_type list = SPLINE_LIST_ARRAY_ELT (splines, this_list);
  455.  
  456.       for (this_spline = 0; this_spline < SPLINE_LIST_LENGTH (list);
  457.            this_spline++)
  458.         {
  459.           spline_type s = SPLINE_LIST_ELT (list, this_spline);
  460.  
  461.           if (SPLINE_DEGREE (s) == LINEAR)
  462.             {
  463.               coordinate_type start
  464.                 = real_cartesian_to_offset_x (START_POINT (s));
  465.               coordinate_type end
  466.                 = real_cartesian_to_offset_x (END_POINT (s));
  467.  
  468.               XDrawLine (display, pixmap, gc, X_LINE (start, end));
  469.             }
  470.  
  471.           else if (SPLINE_DEGREE (s) == CUBIC)
  472.             digitize_spline (s);
  473.  
  474.           else
  475.             FATAL1 ("x_output_char: Spline with degree %d", SPLINE_DEGREE (s));
  476.         }
  477.     }
  478.  
  479.   /* If desired, make sure everything is visible, then wait for the user
  480.      to hit return, so s/he can look at the results.  */
  481.   if (!display_continue)
  482.     {
  483.       send_event (server_window, foserver_update_pixmap_atom, pixmap);
  484.       XSync (display, False);
  485.       
  486.       if (verbose) putchar ('\n');
  487.       printf ("RET to continue: " );
  488.       (void) getchar ();
  489.     }
  490. }
  491.  
  492. /* Rasterize the spline S using forward differences, in floating point,
  493.    non-adaptively.  This is slow compared to the integer, adaptive,
  494.    version, but it's easier to program, and the speed is good enough for
  495.    our purposes (i.e., non-interactive).  A brief but readable
  496.    description of the algorithm is in ``Fast generation of outline
  497.    characters'', by J. Gonczarowski, in the proceedings of the 1989
  498.    conference on raster imaging and digital typography (page 100).  More
  499.    of the math is spelled out in @cite{Fundamentals of interactive
  500.    computer graphics}, by J.D. Foley and A. Van Dam (pp.531-534).  */
  501.    
  502. static void
  503. digitize_spline (spline_type s)
  504. {
  505.   coordinate_type start = real_cartesian_to_offset_x (START_POINT (s)),
  506.                   control1 = real_cartesian_to_offset_x (CONTROL1 (s)),
  507.                   control2 = real_cartesian_to_offset_x (CONTROL2 (s)),
  508.                   end = real_cartesian_to_offset_x (END_POINT (s));
  509.  
  510.   /* These a_i are the coefficients of the polynomial
  511.      p(t) = a_3 t^3 + a_2 t^2 + a_1 t + a_0,
  512.      computed by expanding the Bernshte\u in polynomial 
  513.      z(t) = (1 - t)^3z_1 + 3(1 - t)^2tz_2 + 3(1 - t)t^2z_3 + t^3z_4,
  514.      where z_1 is the starting point of the spline, z_2 and z_3 the
  515.      control points, and z_4 the ending point.  We have two such
  516.      polynomials p(t), one for the x-coordinate, one for the
  517.      y-coordinate.  So everything here is done with points and vectors,
  518.      instead of just numbers.
  519.      
  520.      a_0 = x_0
  521.      a_1 = 3(x_1 - x_0)
  522.      a_2 = 3(x_0 + x_2 - 2x_1)
  523.      a_3 = x_3 - x_0 + 3(x_1 - x_2)  */
  524.      
  525.   coordinate_type /* a0 = start, */
  526.     a1 = IPmult_scalar (IPsubtractP (control1, start), 3),
  527.     a2 = IPmult_scalar (IPsubtractP (IPadd (start, control2),
  528.                                      IPmult_scalar (control1, 2)),
  529.                         3),
  530.     a3 = IPadd (IPsubtractP (end, start),
  531.                 IPmult_scalar (IPsubtractP (control1, control2), 3));
  532.  
  533.   /* The step size.  We want to use the length of the bounding rectangle
  534.      to compute this, instead of the distance between the starting point
  535.      and the ending point, since the latter will be zero if the spline
  536.      is cyclic.  */
  537.   real factor = int_distance (control1, start)
  538.                 + int_distance (control2, control1)
  539.                 + int_distance (end, control2)
  540.                 + int_distance (start, end),
  541.        /* Avoid division by zero, in pathological cases.  (We will just
  542.           produce one point.  */
  543.        delta = 1.0 / MAX (factor, 1),
  544.        delta_squared = delta * delta,
  545.        delta_cubed = delta_squared * delta;
  546.  
  547.   /* The current position.  */
  548.   coordinate_type p = start,
  549.                   previous_p = { start.x - 1, 0 };
  550.   /* The real current position.  */
  551.   real_coordinate_type real_p = int_to_real_coord (p);
  552.  
  553.   /* The first three forward differences evaluated at t = 0.  */
  554.   vector_type d = make_vector (Padd (IPmult_real (a3, delta_cubed), 
  555.                                      Padd (IPmult_real (a2, delta_squared),
  556.                                            IPmult_real (a1, delta)))),
  557.               d2 = make_vector (Padd (IPmult_real (a3, 6 * delta_cubed),
  558.                                       IPmult_real (a2, 2 * delta_squared))),
  559.               d3 = make_vector (IPmult_real (a3, 6 * delta_cubed));
  560.  
  561.   /* Where we will collect the points to output.  */
  562.   unsigned point_count = 0;
  563.   XPoint point_list[(unsigned) (1.0 / delta) + 1];
  564.  
  565.   real t;
  566.  
  567.   for (t = 0.0; t <= 1.0; t += delta)
  568.     {
  569.       if (!IPequal (p, previous_p))
  570.         {
  571.           point_list[point_count].x = p.x;
  572.           point_list[point_count].y = p.y;
  573.           point_count++;
  574.           previous_p = p;
  575.         }
  576.  
  577.       /* We must keep track of the current position in unrounded 
  578.          coordinates, so the calculations do not lose precision.  */
  579.       real_p = Padd_vector (real_p, d);
  580.       p.x = ROUND (real_p.x);
  581.       p.y = ROUND (real_p.y);
  582.       d = Vadd (d, d2);
  583.       d2 = Vadd (d2, d3);
  584.       /* d3 is constant with respect to t.  */
  585.     }
  586.  
  587. #if 0
  588.   /* GDB under system V doesn't grok XPoint, so here's a way to print
  589.      out the points we find.  */
  590.   {
  591.     unsigned i;
  592.     for (i = 0; i < point_count; i++)
  593.       printf ("(%d,%d)", point_list[i].x, point_list[i].y);
  594.     puts ("");
  595.   }
  596. #endif
  597.  
  598.   XDrawPoints (display, pixmap, gc, point_list, point_count,
  599.                CoordModeOrigin);
  600. }
  601.  
  602. /* Send a ClientMessage event to the X window W, on the display
  603.    `display'.  The message type is ATOM and the data DATA.  We could
  604.    simply use the semi-global `server_window' (as we do `display'), but
  605.    it proves useful to be able to send messages to other windows for
  606.    testing.  */
  607.  
  608. static void
  609. send_event (Window w, Atom atom, long data)
  610. {
  611.   XEvent e;
  612.   
  613.   e.xclient.type = ClientMessage;
  614.   e.xclient.display = display;
  615.   e.xclient.window = w;
  616.   e.xclient.message_type = atom;
  617.   e.xclient.format = 32;
  618.   e.xclient.data.l[0] = data;
  619.   
  620.   if (XSendEvent (display, w, False, NoEventMask, &e) == 0)
  621.     FATAL1 ("limn: XSendEvent (of %s) failed", XGetAtomName (display, atom));
  622. }
  623.  
  624. /* Convert a single point from Cartesian coordinates, where the origin
  625.    is at the reference point of the character, to X coordinates, where
  626.    the origin is at the upper left corner.  */
  627.  
  628. static coordinate_type 
  629. cartesian_to_x (coordinate_type in_c)
  630. {
  631.   coordinate_type c = in_c;
  632.  
  633.   c.x *= display_pixel_size;
  634.   c.y *= display_pixel_size;
  635.   c.x -= MIN_COL (offset_bb) * display_pixel_size;
  636.   c.y = MAX_ROW (offset_bb) * display_pixel_size - c.y;
  637.  
  638.   return c;
  639. }
  640.  
  641.  
  642. /* Convert a single point, with real-valued coordinates, to X
  643.    coordinates.  By rounding after the multiplications, we can display
  644.    the real coordinates more accurately.  */
  645.  
  646. static coordinate_type
  647. real_cartesian_to_x (real_coordinate_type real_c)
  648. {
  649.   coordinate_type c;
  650.   
  651.   real_c.x *= display_pixel_size;
  652.   real_c.y *= display_pixel_size;
  653.   real_c.x -= MIN_COL (offset_bb) * display_pixel_size;
  654.   real_c.y = MAX_ROW (offset_bb) * display_pixel_size - real_c.y;
  655.  
  656.   c = real_to_int_coord (real_c);
  657.   
  658.   return c;
  659. }
  660.  
  661.  
  662. /* Like `real_cartesian_to_x', but translate the result to the center of
  663.    the display_pixel_size by display_pixel_size rectangle that we turn each
  664.    point into.  This is used for displaying the fitted character, since
  665.    visually it makes more sense to have it go through the centers of the
  666.    rectangles that represent the pixels, instead of the upper left
  667.    corner.  */
  668.  
  669. static coordinate_type
  670. real_cartesian_to_offset_x (real_coordinate_type real_c)
  671. {
  672.   coordinate_type c = real_cartesian_to_x (real_c);
  673.   
  674.   c.x += display_pixel_size / 2;
  675.   c.y += display_pixel_size / 2;
  676.   
  677.   return c;
  678. }
  679.  
  680.  
  681. /* Convert two points, i.e., a line segment, from Cartesian to X
  682.    coordinates.  Because an XSegment is not two XPoints, I didn't see
  683.    any gain in defining another routine to deal with XPoints.  */
  684.  
  685. static XSegment
  686. segment_to_x (XSegment in_s)
  687. {
  688.   XSegment out_s;
  689.   coordinate_type c;
  690.   
  691.   c.x = in_s.x1;
  692.   c.y = in_s.y1;
  693.   c = cartesian_to_x (c);
  694.   out_s.x1 = c.x;
  695.   out_s.y1 = c.y;
  696.   
  697.   c.x = in_s.x2;
  698.   c.y = in_s.y2;
  699.   c = cartesian_to_x (c);
  700.   out_s.x2 = c.x;
  701.   out_s.y2 = c.y;
  702.   
  703.   return out_s;
  704. }
  705.